{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "from torch import nn\n",
    "from torch import optim\n",
    "import numpy as np\n",
    "from torch.nn import functional as F\n",
    "import gym\n",
    "import matplotlib.pyplot as plt\n",
    "from skimage.transform import resize\n",
    "from collections import deque\n",
    "from IPython.display import clear_output, display\n",
    "import torch.multiprocessing as mp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.01239285, -0.00379271, -0.00721933, -0.00299865])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#env = gym.make(\"Pong-v0\")\n",
    "env = gym.make(\"CartPole-v1\")\n",
    "env.reset()\n",
    "#env.unwrapped.get_action_meanings()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'#Test environment with random actions\\nenv.reset()\\n#actions = [0,2,3]\\nfor i in range(2000):\\n    env.render()\\n    a = env.action_space.sample()\\n    #a = np.random.choice(actions)\\n    state, reward, done, info = env.step(a)\\n    if done:\\n        env.reset()\\nenv.close()'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''#Test environment with random actions\n",
    "env.reset()\n",
    "#actions = [0,2,3]\n",
    "for i in range(2000):\n",
    "    env.render()\n",
    "    a = env.action_space.sample()\n",
    "    #a = np.random.choice(actions)\n",
    "    state, reward, done, info = env.step(a)\n",
    "    if done:\n",
    "        env.reset()\n",
    "env.close()'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'state,reward,done,lives=env.step(2)\\nplt.imshow(downscale_obs(state, new_size=(42,42)))\\nprint(reward,done)'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#env.reset()\n",
    "'''state,reward,done,lives=env.step(2)\n",
    "plt.imshow(downscale_obs(state, new_size=(42,42)))\n",
    "print(reward,done)'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#t1=prepare_initial_state(env.render('rgb_array'),3)\n",
    "#t2=prepare_multi_state(t,env.render('rgb_array')).shape\n",
    "#t1.shape,t2.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ActorCritic(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(ActorCritic, self).__init__()\n",
    "        self.l1 = nn.Linear(4,25)\n",
    "        self.l2 = nn.Linear(25,50)\n",
    "        self.actor_lin1 = nn.Linear(50,2)\n",
    "        self.l3 = nn.Linear(50,25)\n",
    "        self.critic_lin1 = nn.Linear(25,1)\n",
    "\n",
    "    def forward(self,x):\n",
    "        x = F.normalize(x,dim=0)\n",
    "        y = F.relu(self.l1(x))\n",
    "        y = F.relu(self.l2(y))\n",
    "        actor = F.log_softmax(self.actor_lin1(y),dim=0)\n",
    "        c = F.relu(self.l3(y.detach()))\n",
    "        critic = torch.tanh(self.critic_lin1(c))\n",
    "        return actor, critic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'x =np.arange(start=10,stop=0,step=-1)\\nprint(x)\\ngamma = 0.9\\nx[0]=-20\\nG = []\\ng = x[0]\\nG.append(g)\\nprint(g)\\nfor i in range(len(x)-1):\\n    g = g + gamma * x[i+1]\\n    G.append(g)\\nG'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''x =np.arange(start=10,stop=0,step=-1)\n",
    "print(x)\n",
    "gamma = 0.9\n",
    "x[0]=-20\n",
    "G = []\n",
    "g = x[0]\n",
    "G.append(g)\n",
    "print(g)\n",
    "for i in range(len(x)-1):\n",
    "    g = g + gamma * x[i+1]\n",
    "    G.append(g)\n",
    "G'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'TestModel = ActorCritic()\\nstate=env.step(1)[0]\\na,_ = TestModel(prepare_initial_state(state))'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''TestModel = ActorCritic()\n",
    "state=env.step(1)[0]\n",
    "a,_ = TestModel(prepare_initial_state(state))'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "def update_params(worker_opt,values,logprobs,rewards,clc=0.1,gamma=0.95):\n",
    "        rewards = torch.Tensor(rewards).flip(dims=(0,)).view(-1)\n",
    "        logprobs = torch.stack(logprobs).flip(dims=(0,)).view(-1) #to Tensor and reverse\n",
    "        values = torch.stack(values).flip(dims=(0,)).view(-1) #to Tensor and reverse\n",
    "        Returns = []\n",
    "        ret_ = torch.Tensor([0])#rewards_[0]\n",
    "        #Ret.append(ret_)\n",
    "        for r in range(rewards.shape[0]):\n",
    "            ret_ = rewards[r] + gamma * ret_\n",
    "            Returns.append(ret_)\n",
    "        Returns = torch.stack(Returns).view(-1)\n",
    "        Returns = F.normalize(Returns,dim=0)\n",
    "        actor_loss = -1*logprobs * (Returns - values.detach())\n",
    "        critic_loss = torch.pow(values - Returns,2)\n",
    "        loss = actor_loss.sum() + clc*critic_loss.sum()\n",
    "        loss.backward()\n",
    "        worker_opt.step()\n",
    "        return actor_loss, critic_loss, len(rewards)\n",
    "        \n",
    "def run_episode(worker_env, worker_model):\n",
    "    state = torch.from_numpy(worker_env.env.state).float()\n",
    "    values, logprobs, rewards = [],[],[]\n",
    "    done = False\n",
    "    j=0\n",
    "    while (done == False):\n",
    "        j+=1\n",
    "        #run actor critic model\n",
    "        policy, value = worker_model(state)\n",
    "        values.append(value)\n",
    "        #sample action\n",
    "        logits = policy.view(-1)\n",
    "        action_dist = torch.distributions.Categorical(logits=logits)\n",
    "        action = action_dist.sample()\n",
    "        logprob_ = policy.view(-1)[action]\n",
    "        logprobs.append(logprob_)\n",
    "        state_, _, done, info = worker_env.step(action.detach().numpy())\n",
    "        state = torch.from_numpy(state_).float()\n",
    "        if done:\n",
    "            reward = -10\n",
    "            worker_env.reset()\n",
    "        else:\n",
    "            reward = 1.0\n",
    "        rewards.append(reward)\n",
    "    return values, logprobs, rewards\n",
    "\n",
    "def worker(t, worker_model, counter, params, losses): #q is mp Queue\n",
    "    print(\"In process {}\".format(t,))\n",
    "    start_time = time.time()\n",
    "    #play n steps of the game, store rewards\n",
    "    worker_env = gym.make(\"CartPole-v1\")\n",
    "    worker_env.reset()\n",
    "    worker_opt = optim.Adam(lr=1e-4,params=worker_model.parameters())\n",
    "    worker_opt.zero_grad()\n",
    "    for i in range(params['epochs']):\n",
    "        worker_opt.zero_grad()\n",
    "        #stores\n",
    "        values, logprobs, rewards = run_episode(worker_env,worker_model)\n",
    "        actor_loss,critic_loss,eplen = update_params(worker_opt,values,logprobs,rewards)\n",
    "        counter.value = counter.value + 1\n",
    "        losses.put(eplen)\n",
    "        if i % 50 == 0:\n",
    "            print(\"Process: {} Maxrun: {} ALoss: {} CLoss: {}\".format(t,eplen, \\\n",
    "                      actor_loss.detach().mean().numpy(),critic_loss.detach().mean().numpy()))\n",
    "        if time.time() - start_time > 45:\n",
    "            print(\"Done 45 seconds\")\n",
    "            break;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'v=torch.arange(start=5,end=0,step=-1)\\nprint(v[:-1],v[1:])'"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''v=torch.arange(start=5,end=0,step=-1)\n",
    "print(v[:-1],v[1:])'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"%%time\\nTestModel = ActorCritic()\\nworker_opt = optim.Adam(lr=1e-4,params=TestModel.parameters())\\nq2 = mp.Value('i',0)\\nparams = {\\n    'epochs':5,\\n    'n_steps':5,\\n    'n_workers':1,\\n}\\nAC_step(0,TestModel,q2,params)\""
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''%%time\n",
    "TestModel = ActorCritic()\n",
    "worker_opt = optim.Adam(lr=1e-4,params=TestModel.parameters())\n",
    "q2 = mp.Value('i',0)\n",
    "params = {\n",
    "    'epochs':5,\n",
    "    'n_steps':5,\n",
    "    'n_workers':1,\n",
    "}\n",
    "AC_step(0,TestModel,q2,params)'''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <span style=\"color:red;\">Train</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "In process 0\n",
      "In process 1\n",
      "In process 2\n",
      "In process 3\n",
      "In process 4\n",
      "In process 5\n",
      "In process 6\n",
      "Process: 3 Maxrun: 28 ALoss: 0.060241956263780594 CLoss: 0.03418976068496704\n",
      "Process: 6 Maxrun: 26 ALoss: 0.05034090578556061 CLoss: 0.034537963569164276\n",
      "Process: 0 Maxrun: 43 ALoss: 0.05275442823767662 CLoss: 0.01969188079237938\n",
      "Process: 5 Maxrun: 39 ALoss: 0.051650065928697586 CLoss: 0.02143673412501812\n",
      "Process: 1 Maxrun: 58 ALoss: 0.049718618392944336 CLoss: 0.015436741523444653\n",
      "Process: 4 Maxrun: 42 ALoss: 0.05072680115699768 CLoss: 0.019803021103143692\n",
      "Process: 2 Maxrun: 50 ALoss: 0.05277305468916893 CLoss: 0.01792260631918907\n",
      "Process: 0 Maxrun: 24 ALoss: 0.02283882163465023 CLoss: 0.03526848554611206\n",
      "Process: 2 Maxrun: 22 ALoss: 0.0008543675648979843 CLoss: 0.03945693373680115\n",
      "Process: 6 Maxrun: 32 ALoss: 0.036029357463121414 CLoss: 0.023679405450820923\n",
      "Process: 5 Maxrun: 15 ALoss: -0.10158392041921616 CLoss: 0.07822556048631668\n",
      "Process: 3 Maxrun: 30 ALoss: 0.021618677303195 CLoss: 0.027394281700253487\n",
      "Process: 4 Maxrun: 32 ALoss: 0.029533477500081062 CLoss: 0.02302265167236328\n",
      "Process: 1 Maxrun: 17 ALoss: -0.05076867714524269 CLoss: 0.06339637190103531\n",
      "Process: 0 Maxrun: 11 ALoss: -0.28480014204978943 CLoss: 0.1413124054670334\n",
      "Process: 5 Maxrun: 44 ALoss: 0.03695300966501236 CLoss: 0.00968565046787262\n",
      "Process: 6 Maxrun: 17 ALoss: -0.03939417377114296 CLoss: 0.057618822902441025\n",
      "Process: 1 Maxrun: 48 ALoss: 0.029061928391456604 CLoss: 0.009974260814487934\n",
      "Process: 2 Maxrun: 46 ALoss: 0.03145596385002136 CLoss: 0.011202245019376278\n",
      "Process: 3 Maxrun: 21 ALoss: -0.000474670814583078 CLoss: 0.04486386477947235\n",
      "Process: 4 Maxrun: 34 ALoss: 0.03306642174720764 CLoss: 0.020633239299058914\n",
      "Process: 6 Maxrun: 52 ALoss: -0.0017306023510172963 CLoss: 0.012758336029946804\n",
      "Process: 0 Maxrun: 41 ALoss: 0.006299195811152458 CLoss: 0.013157698325812817\n",
      "Process: 5 Maxrun: 13 ALoss: -0.13425566256046295 CLoss: 0.09599894285202026\n",
      "Process: 2 Maxrun: 98 ALoss: 0.008891502395272255 CLoss: 0.0022165398113429546\n",
      "Process: 1 Maxrun: 33 ALoss: 0.011883032508194447 CLoss: 0.02047375775873661\n",
      "Process: 3 Maxrun: 51 ALoss: 0.019242363050580025 CLoss: 0.008803240023553371\n",
      "Process: 4 Maxrun: 75 ALoss: 0.004387926775962114 CLoss: 0.0044268337078392506\n",
      "Process: 0 Maxrun: 67 ALoss: 0.006034068297594786 CLoss: 0.004469088278710842\n",
      "Process: 6 Maxrun: 69 ALoss: 0.0023957628291100264 CLoss: 0.004684064071625471\n",
      "Process: 2 Maxrun: 41 ALoss: -0.0031578736379742622 CLoss: 0.01256165374070406\n",
      "Process: 5 Maxrun: 85 ALoss: -0.0015988488448783755 CLoss: 0.00242494884878397\n",
      "Process: 3 Maxrun: 98 ALoss: -0.014946148730814457 CLoss: 0.0027197187300771475\n",
      "Process: 1 Maxrun: 118 ALoss: -0.011550398543477058 CLoss: 0.0016195019707083702\n",
      "Process: 4 Maxrun: 54 ALoss: 0.01084556058049202 CLoss: 0.007465990725904703\n",
      "Process: 0 Maxrun: 100 ALoss: -0.0008733564172871411 CLoss: 0.002155138412490487\n",
      "Process: 6 Maxrun: 91 ALoss: 0.008740069344639778 CLoss: 0.0023427826818078756\n",
      "Process: 3 Maxrun: 174 ALoss: -0.004715921822935343 CLoss: 0.0008168267668224871\n",
      "Process: 5 Maxrun: 77 ALoss: -0.0008503157296217978 CLoss: 0.003973084501922131\n",
      "Process: 2 Maxrun: 55 ALoss: 0.014725630171597004 CLoss: 0.006250392645597458\n",
      "Process: 1 Maxrun: 92 ALoss: -0.0038813301362097263 CLoss: 0.0022914663422852755\n",
      "Process: 4 Maxrun: 164 ALoss: -0.012117579579353333 CLoss: 0.0015526035567745566\n",
      "Process: 0 Maxrun: 96 ALoss: 0.008075594902038574 CLoss: 0.0027395598590373993\n",
      "Done 45 seconds\n",
      "Done 45 seconds\n",
      "Done 45 seconds\n",
      "Done 45 seconds\n",
      "Process: 6 Maxrun: 141 ALoss: 0.0048079355619847775 CLoss: 0.0010050807613879442\n",
      "Done 45 seconds\n",
      "Done 45 seconds\n",
      "Done 45 seconds\n",
      "2050 0\n",
      "CPU times: user 63.6 ms, sys: 76.1 ms, total: 140 ms\n",
      "Wall time: 45.4 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "if __name__ == '__main__':\n",
    "    MasterNode = ActorCritic()\n",
    "    MasterNode.share_memory()\n",
    "    processes = []\n",
    "    params = {\n",
    "        'epochs':1000,\n",
    "        'n_workers':7,\n",
    "    }\n",
    "    counter = mp.Value('i',0)\n",
    "    losses = mp.Queue()\n",
    "    for i in range(params['n_workers']):\n",
    "        p = mp.Process(target=worker, args=(i,MasterNode,counter,params,losses))\n",
    "        p.start()\n",
    "        processes.append(p)\n",
    "    for p in processes:\n",
    "        p.join()\n",
    "    for p in processes:\n",
    "        p.terminate()\n",
    "\n",
    "    print(counter.value,processes[1].exitcode)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "losses_ = []\n",
    "while not losses.empty():\n",
    "    losses_.append(losses.get())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 648x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(9,5))\n",
    "x = np.array(losses_)\n",
    "N = 50\n",
    "x = np.convolve(x, np.ones((N,))/N, mode='valid')\n",
    "plt.ylabel(\"Mean Episode Length\")\n",
    "plt.xlabel(\"Training Time\")\n",
    "plt.title(\"CartPole Training Evaluation\")\n",
    "plt.plot(x)\n",
    "#plt.savefig(\"avg_rewards.png\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Maxrun: 145.08\n"
     ]
    }
   ],
   "source": [
    "steps = 2000\n",
    "env = gym.make(\"CartPole-v1\")\n",
    "env.reset()\n",
    "maxrun = 0\n",
    "state = torch.from_numpy(env.env.state).float()\n",
    "done = False\n",
    "avg_run = 0\n",
    "runs = int(25)\n",
    "for i in range(runs):\n",
    "    maxrun = 0\n",
    "    done = False\n",
    "    env.reset()\n",
    "    state = torch.from_numpy(env.env.state).float()\n",
    "    while(done==False):\n",
    "        #env.render('human')\n",
    "        policy, value = MasterNode(state)\n",
    "        #sample action\n",
    "        action = torch.distributions.Categorical(logits=policy.view(-1)).sample().detach().numpy()\n",
    "        state_, reward, done, lives = env.step(action)\n",
    "        \n",
    "        env.render()\n",
    "        #print(value,reward)\n",
    "        state = torch.from_numpy(state_).float()\n",
    "        maxrun += 1\n",
    "    avg_run += maxrun\n",
    "avg_run = avg_run / runs\n",
    "env.close()\n",
    "print(\"Maxrun: {}\".format(avg_run,))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'TestModel = ActorCritic()\\nenv = gym.make(\"CartPole-v1\")\\nenv.reset()\\nmaxrun = 0\\nstate = torch.from_numpy(env.env.state).float()\\ndone = False\\navg_run = 0\\nruns = int(200)\\nfor i in range(runs):\\n    maxrun = 0\\n    done = False\\n    env.reset()\\n    state = torch.from_numpy(env.env.state).float()\\n    while(done==False):\\n        #env.render(\\'human\\')\\n        policy, value = TestModel(state)\\n        #sample action\\n        action = torch.distributions.Categorical(logits=policy.view(-1)).sample()\\n        state_, reward, done, lives = env.step(env.action_space.sample())\\n        state = torch.from_numpy(state_).float()\\n        maxrun += 1\\n    avg_run += maxrun\\navg_run /= runs\\nenv.close()\\nprint(\"Maxrun: {}\".format(avg_run,))'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''TestModel = ActorCritic()\n",
    "env = gym.make(\"CartPole-v1\")\n",
    "env.reset()\n",
    "maxrun = 0\n",
    "state = torch.from_numpy(env.env.state).float()\n",
    "done = False\n",
    "avg_run = 0\n",
    "runs = int(200)\n",
    "for i in range(runs):\n",
    "    maxrun = 0\n",
    "    done = False\n",
    "    env.reset()\n",
    "    state = torch.from_numpy(env.env.state).float()\n",
    "    while(done==False):\n",
    "        #env.render('human')\n",
    "        policy, value = TestModel(state)\n",
    "        #sample action\n",
    "        action = torch.distributions.Categorical(logits=policy.view(-1)).sample()\n",
    "        state_, reward, done, lives = env.step(env.action_space.sample())\n",
    "        state = torch.from_numpy(state_).float()\n",
    "        maxrun += 1\n",
    "    avg_run += maxrun\n",
    "avg_run /= runs\n",
    "env.close()\n",
    "print(\"Maxrun: {}\".format(avg_run,))'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
